NAT インスタンス用の AMI を Packer で作ってみた
コンバンハ、千葉(幸)です。
先日、NAT インスタンスを Amazon Linux2 AMI から手作りしてみました。
久しぶりに OS 上でコマンドを叩いてテンションが上がったので、これまた久しぶりに Packer を使ってみるか、ということでパカることにしました。
今回やること
大まかに書くと以下の通りです。
- Packer により NAT インスタンス用 AMI をビルド
- NAT インスタンスのセットアップ
- クライアントインスタンスから疎通確認
NAT インスタンスを配置する VPC やクライアントインスタンスはあらかじめ作成済みの前提とします。
1. Packer によるビルド
今回使用する Packer は以下バージョンです。
% packer --version 1.8.4
あらかじめ、必要な権限を有する IAM ユーザーのクレデンシャルを AWS CLI プロファイルに設定しています。 *1
テンプレートの準備
nat-instance.json
という名称で以下のテンプレートを準備しました。
{ "builders": [ { "type": "amazon-ebs", "region": "ap-northeast-1", "instance_type": "t3.micro", "ssh_username": "ec2-user", "source_ami_filter": { "filters": { "name": "amzn2-ami-hvm-*-x86_64-gp2" }, "owners": ["137112412989"], "most_recent": true }, "associate_public_ip_address": true, "ami_name": "NAT-AmazonLinux2-{{isotime | clean_resource_name}}", "tags": { "Base_AMI_ID": "{{ .SourceAMI }}", "Base_AMI_NAME": "{{ .SourceAMIName }}" } } ], "provisioners": [ { "type": "shell", "inline": [ "sudo sysctl -w net.ipv4.ip_forward=1 | sudo tee -a /etc/sysctl.conf", "sudo /sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE", "sudo yum install -y iptables-services", "sudo service iptables save", "sudo systemctl enable iptables" ] } ] }
このテンプレートにより、簡単に言うと以下が行われます。
- Amazon Linux2 の最新の AMI をベースに一時的なインスタンスを起動
- クライアントから一時的なインスタンスに ec2-user で SSH 接続し各種コマンドを実行
- 実行後 AMI を取得し、AMI 名の末尾に作成時刻を付与
- AMI 作成完了後、一時的なインスタンスを削除
一時的なインスタンスの起動サブネットを指定していないので、デフォルトの VPC で作成が行われます。
テンプレートの検証
packer validet
の後にテンプレートファイルを指定することで検証できます。
% packer validate nat-instance.json The configuration is valid.
おまけ:Packer テンプレートを Json から HCL2 に変換
Packer のテンプレートは Json だけでなく HCL2(HashiCorp Configuration Language 2)もサポートされています。
個人的に Json のほうが慣れているのですが、簡単に変換してくれるコマンドがあるので試してみます。
% packer hcl2_upgrade nat-instance.json Successfully created nat-instance.json.pkr.hcl. Exit 0
変換後のテンプレートはこのようになっていました。一部変換が正常に行われていないですね……。
% cat nat-instance.json.pkr.hcl data "amazon-ami" "autogenerated_1" { filters = { name = "amzn2-ami-hvm-*-x86_64-gp2" } most_recent = true owners = ["137112412989"] region = "ap-northeast-1" } # 1 error occurred upgrading the following block: # unhandled "clean_resource_name" call: # there is no way to automatically upgrade the "clean_resource_name" call. # Please manually upgrade to use custom validation rules, `replace(string, substring, replacement)` or `regex_replace(string, substring, replacement)` # Visit https://packer.io/docs/templates/hcl_templates/variables#custom-validation-rules , https://www.packer.io/docs/templates/hcl_templates/functions/string/replace or https://www.packer.io/docs/templates/hcl_templates/functions/string/regex_replace for more infos. source "amazon-ebs" "autogenerated_1" { ami_name = "NAT-AmazonLinux2-{{ clean_resource_name `${timestamp()}` }}" associate_public_ip_address = true instance_type = "t3.micro" region = "ap-northeast-1" source_ami = "${data.amazon-ami.autogenerated_1.id}" ssh_username = "ec2-user" tags = { Base_AMI_ID = "{{ .SourceAMI }}" Base_AMI_NAME = "{{ .SourceAMIName }}" } } build { sources = ["source.amazon-ebs.autogenerated_1"] provisioner "shell" { inline = ["sudo sysctl -w net.ipv4.ip_forward=1 | sudo tee -a /etc/sysctl.conf", "sudo /sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE", "sudo yum install -y iptables-services", "sudo service iptables save", "sudo systemctl enable iptables"] } }
(読み方が……読み方がわからん……!!!)
おとなしく諦めて Json のテンプレートを引き続き使用することにします。
Packer のビルドの実行
packer build テンプレート名
でビルドが実行できます。
今回は一連の流れが 3 分ほどで完了しました。ログを眺めていると何をしているのかが見えて楽しいです。
% packer build nat-instance.json amazon-ebs: output will be in this color. ==> amazon-ebs: Prevalidating any provided VPC information ==> amazon-ebs: Prevalidating AMI Name: NAT-AmazonLinux2-2022-11-07T13-45-43Z amazon-ebs: Found Image ID: ami-046a961f907758d0d ==> amazon-ebs: Creating temporary keypair: packer_63690c07-48c7-f172-95ef-9e0825e6879b ==> amazon-ebs: Creating temporary security group for this instance: packer_63690c09-f5ea-c49c-c1ce-e5cc509d2f98 ==> amazon-ebs: Authorizing access to port 22 from [0.0.0.0/0] in the temporary security groups... ==> amazon-ebs: Launching a source AWS instance... amazon-ebs: Instance ID: i-0258b976379c25086 ==> amazon-ebs: Waiting for instance (i-0258b976379c25086) to become ready... ==> amazon-ebs: Using SSH communicator to connect: 35.77.64.135 ==> amazon-ebs: Waiting for SSH to become available... ==> amazon-ebs: Connected to SSH! ==> amazon-ebs: Provisioning with shell script: /var/folders/_5/20lflls12nd16km90bdpry700000gp/T/packer-shell2491648282 amazon-ebs: net.ipv4.ip_forward = 1 amazon-ebs: Loaded plugins: extras_suggestions, langpacks, priorities, update-motd ==> amazon-ebs: Existing lock /var/run/yum.pid: another copy is running as pid 2297. ==> amazon-ebs: Another app is currently holding the yum lock; waiting for it to exit... ==> amazon-ebs: The other application is: yum ==> amazon-ebs: Memory : 161 M RSS (454 MB VSZ) ==> amazon-ebs: Started: Mon Nov 7 13:46:01 2022 - 00:07 ago ==> amazon-ebs: State : Running, pid: 2297 ==> amazon-ebs: Another app is currently holding the yum lock; waiting for it to exit... ==> amazon-ebs: The other application is: yum ==> amazon-ebs: Memory : 169 M RSS (461 MB VSZ) ==> amazon-ebs: Started: Mon Nov 7 13:46:01 2022 - 00:09 ago ==> amazon-ebs: State : Running, pid: 2297 amazon-ebs: Resolving Dependencies amazon-ebs: --> Running transaction check amazon-ebs: ---> Package iptables-services.x86_64 0:1.8.4-10.amzn2.1.2 will be installed amazon-ebs: --> Finished Dependency Resolution amazon-ebs: amazon-ebs: Dependencies Resolved amazon-ebs: amazon-ebs: ================================================================================ amazon-ebs: Package Arch Version Repository Size amazon-ebs: ================================================================================ amazon-ebs: Installing: amazon-ebs: iptables-services x86_64 1.8.4-10.amzn2.1.2 amzn2-core 58 k amazon-ebs: amazon-ebs: Transaction Summary amazon-ebs: ================================================================================ amazon-ebs: Install 1 Package amazon-ebs: amazon-ebs: Total download size: 58 k amazon-ebs: Installed size: 24 k amazon-ebs: Downloading packages: amazon-ebs: Running transaction check amazon-ebs: Running transaction test amazon-ebs: Transaction test succeeded amazon-ebs: Running transaction amazon-ebs: Installing : iptables-services-1.8.4-10.amzn2.1.2.x86_64 1/1 amazon-ebs: Verifying : iptables-services-1.8.4-10.amzn2.1.2.x86_64 1/1 amazon-ebs: amazon-ebs: Installed: amazon-ebs: iptables-services.x86_64 0:1.8.4-10.amzn2.1.2 amazon-ebs: amazon-ebs: Complete! amazon-ebs: iptables: Saving firewall rules to /etc/sysconfig/iptables: [ OK ] ==> amazon-ebs: Created symlink from /etc/systemd/system/basic.target.wants/iptables.service to /usr/lib/systemd/system/iptables.service. ==> amazon-ebs: Stopping the source instance... amazon-ebs: Stopping instance ==> amazon-ebs: Waiting for the instance to stop... ==> amazon-ebs: Creating AMI NAT-AmazonLinux2-2022-11-07T13-45-43Z from instance i-0258b976379c25086 amazon-ebs: AMI: ami-0457c2a5f19548b0f ==> amazon-ebs: Waiting for AMI to become ready... ==> amazon-ebs: Skipping Enable AMI deprecation... ==> amazon-ebs: Adding tags to AMI (ami-0457c2a5f19548b0f)... ==> amazon-ebs: Tagging snapshot: snap-0407c0fb3a23b2d7a ==> amazon-ebs: Creating AMI tags amazon-ebs: Adding tag: "Base_AMI_ID": "ami-046a961f907758d0d" amazon-ebs: Adding tag: "Base_AMI_NAME": "amzn2-ami-hvm-2.0.20221004.0-x86_64-gp2" ==> amazon-ebs: Creating snapshot tags ==> amazon-ebs: Terminating the source AWS instance... ==> amazon-ebs: Cleaning up any extra volumes... ==> amazon-ebs: No volumes to clean up, skipping ==> amazon-ebs: Deleting temporary security group... ==> amazon-ebs: Deleting temporary keypair... Build 'amazon-ebs' finished after 2 minutes 51 seconds. ==> Wait completed after 2 minutes 51 seconds ==> Builds finished. The artifacts of successful builds are: --> amazon-ebs: AMIs were created: ap-northeast-1: ami-0457c2a5f19548b0f
指定した名称・タグで AMI が作成されていました。
2. NAT インスタンスのセットアップ
NAT インスタンスの起動
Packer により作成された AMI からインスタンスを起動します。
- パブリックサブネットに配置しパブリック IP 有効化
- IAM ロールをアタッチしてSystems Manager の権限付与
- t3.micro、gp3、8 GiB
意図した設定値になっているかの確認のため、ユーザーデータに以下を指定しました。
#!/bin/bash sysctl -n net.ipv4.ip_forward systemctl status iptables iptables -t nat --list
EC2 コンソールからシステムログを取得して確認すると、きちんと意図通りになっていることが確認できました。 *2
...略... ip-172-31-27-71 login: [ 15.908072] cloud-init[2330]: Cloud-init v. 19.3-45.amzn2 running 'modules:final' at Mon, 07 Nov 2022 14:03:51 +0000. Up 15.85 seconds. [ 15.922281] cloud-init[2330]: 1 [ 15.927948] cloud-init[2330]: â iptables.service - IPv4 firewall with iptables [ 15.928235] cloud-init[2330]: Loaded: loaded (/usr/lib/systemd/system/iptables.service; enabled; vendor preset: disabled) [ 15.928589] cloud-init[2330]: Active: active (exited) since Mon 2022-11-07 14:03:42 UTC; 9s ago [ 15.929005] cloud-init[2330]: Process: 1695 ExecStart=/usr/libexec/iptables/iptables.init start (code=exited, status=0/SUCCESS) [ 15.929429] cloud-init[2330]: Main PID: 1695 (code=exited, status=0/SUCCESS) [ 15.929828] cloud-init[2330]: CGroup: /system.slice/iptables.service [ 15.930259] cloud-init[2330]: Nov 07 14:03:41 ip-172-31-19-34.ap-northeast-1.compute.internal systemd[1]: Starting IPv4 firewall with iptables... [ 15.930824] cloud-init[2330]: Nov 07 14:03:42 ip-172-31-19-34.ap-northeast-1.compute.internal iptables.init[1695]: iptables: Applying firewall rules: [ OK ] [ 15.931194] cloud-init[2330]: Nov 07 14:03:42 ip-172-31-19-34.ap-northeast-1.compute.internal systemd[1]: Started IPv4 firewall with iptables. [ 15.933461] cloud-init[2330]: Chain PREROUTING (policy ACCEPT) [ 15.933902] cloud-init[2330]: target prot opt source destination [ 15.934274] cloud-init[2330]: Chain INPUT (policy ACCEPT) [ 15.934690] cloud-init[2330]: target prot opt source destination [ 15.935103] cloud-init[2330]: Chain OUTPUT (policy ACCEPT) [ 15.935520] cloud-init[2330]: target prot opt source destination [ 15.935948] cloud-init[2330]: Chain POSTROUTING (policy ACCEPT) [ 15.936396] cloud-init[2330]: target prot opt source destination [ 15.936770] cloud-init[2330]: MASQUERADE all -- anywhere anywhere ...略...
送信元/送信先チェックの無効化
デフォルトでは送信元/送信先チェックが有効になっているので、「アクション」から無効にしておきます。
ちなみにインスタンスの起動時にこれをコントロールできたりはしないんですね。(以下はインスタンス起動ウイザードの「高度なネットワーク設定」の画面。)
3. クライアントインスタンスからの疎通確認
クライアントインスタンスが属するサブネットのルートテーブル編集
クライアントインスタンスはプライベートサブネットに属しています。
そのサブネットのルートテーブルで、宛先を0.0.0.0/0
としターゲットを NAT インスタンスとするルートを追加します。
ルートの編集画面では「インスタンス」でも「ネットワークインターフェース」でも指定できますが、保存は「ネットワークインターフェース」に統一されます。
クライアントインスタンスにセッションマネージャー接続
クライアントインスタンスにセッションマネージャー接続を試みます。SSM エージェントのインストール、IAM ロールでの権限の付与は済んでいるので、NAT インスタンス経由でエンドポイントにアウトバウンド通信が確立できれば接続できる状態です。
問題なく接続できました。また、checkip.amazonaws.com
宛ての curl も成功し、NAT インスタンスのグローバル IP アドレスが返却されました。
終わりに
NAT インスタンス用の AMI を Packer で作ってみました。
たまに触ると Packer 面白いですね。パカっという感じがします。似たようなことをされようとしている方の参考になれば幸いです。
以上、 チバユキ (@batchicchi) がお送りしました。